home *** CD-ROM | disk | FTP | other *** search
- /*
- HASMovableModal.c from Hsoi's App Shell ©1995-1997 John C. Daub. All rights reserved.
-
- This file is a nice "all in one" unit that allows you to impliment a movable
- modal dialog with ease...well, almost.
-
- To begin, this source is actually not my work, but rather the work of Marco Piovanelli.
- This is from his MovableModal library, version 2.0. However, to get the code to work
- "nicely" with Hsoi's App Shell, I had to modify things here and there.
-
- Plus, I wanted to move towards Copland compatability...so, I added that in as best
- as I could. And, I also tried to add in more comments to the code to make it
- more understandable to the casual reader.
-
- Furthermore, and I dunno if the problem lies with me or Marco's code (haven't had the
- time to do in-depth investigation right now), but I can't get the disabling of
- the menu bar to work...perhaps cause I'm doing something "wrong" in my code.
-
- Anyways, I use this, currently, to display the "Other Font Size" dialog box...and
- deal with things fine. Try it out and all credits to Marco for it.
-
- Copyright © 1993-1995 Marco Piovanelli
- <URL:mailto:piovanel@dsi.unimi.it>
- */
-
- #pragma mark ••• #includes •••
-
- // make sure we have some needed Universal Headers
-
-
- #ifndef __TYPES__
- #include <Types.h>
- #endif
-
- #ifndef __EVENTS__
- #include <Events.h>
- #endif
-
- #ifndef __WINDOWS__
- #include <Windows.h>
- #endif
-
- #ifndef __TEXTEDIT__
- #include <TextEdit.h>
- #endif
-
- #ifndef __MENUS__
- #include <Menus.h>
- #endif
-
- #ifndef __DIALOGS__
- #include <Dialogs.h>
- #endif
-
- #ifndef __SCRAP__
- #include <Scrap.h>
- #endif
-
- #ifndef __BALLOONS__
- #include <Balloons.h>
- #endif
-
- #ifndef __LOWMEM__
- #include <LowMem.h>
- #endif
-
- #ifndef __GESTALT__
- #include <Gestalt.h>
- #endif
-
-
- // and don't forget the local header
-
- #include "HASMovableModal.h"
-
- // if you use Michael Kamprath's WASTE Object Handlers, you'll need to include this
-
- #include "WASTE_Objects.h"
-
-
- #pragma mark -
- #pragma mark ••• Constants •••
-
- enum {
- kSystemMenuThreshold = -16000, // certain system menus (like the application, help,
- // and keyboard menus) have ID's below this number,
- // and we don't want to affect those menus in a
- // movable modal dialog
-
- kMovableModalEventMask = mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask
- // this is an event mask to filter out the stuff we want/don't want
- };
-
- #pragma mark -
- #pragma mark ••• Structs •••
-
-
- // some structs to hold internal data
-
- // I added this #pragma in here to help struct alignment for 68k/PPC code happiness
-
- #if PRAGMA_ALIGN_SUPPORTED
- #pragma options align=mac68k
- #endif
-
-
- typedef struct {
- MenuRef hMenu;
- short leftEdge;
- } MenuEntry;
-
- typedef struct {
- short offsetToLastMenu;
- short rightmostEdge;
- short unused;
- MenuEntry theMenus[1];
- } MenuList, *MenuListPtr, **MenuListHandle;
-
- typedef struct {
- unsigned long mbsBarEnable;
- unsigned long mbsEditEnable;
- short mbsEditMenuID;
- short mbsCutItem;
- short mbsCopyItem;
- short mbsPasteItem;
- Boolean mbsIsDisabled;
- } MenuBarState;
-
-
- #if PRAGMA_ALIGN_SUPPORTED
- #pragma options align=reset
- #endif
-
- #pragma mark -
- #pragma mark ••• Globals •••
-
- // a nice global instance of our MenuBarState, initalized happily
-
- static MenuBarState barState = { 0, 0, 0, 0, 0, 0, false };
-
- #pragma mark -
- #pragma mark ••• Static prototypes •••
-
- // some local/internal function prototypes
-
- short GetMenuItemFromKeyEquiv(MenuRef menu, short keyEquiv);
- Boolean DoMenuChoice(DialogRef dialog, short *item, long menuChoice);
- Boolean HandleMouseDown(DialogRef dialog, EventRecord *event, short *item);
-
- #pragma mark -
- #pragma mark ••• Menus •••
-
- // given a menu and a keyboard eqivalent (e.g. keyEquiv = X; for the Cut command)
- // find the menu item that corresponds to this equivalent. This is nice cause you
- // can't always assume menus will be set up in the same way (e.g. "usually" Cut is
- // the 3rd item in the Edit menu, but what if there was no seperator line between
- // Undo and Cut? it'd be the 2nd...this is handy for prevention of assuming
- // cause when you assume.... (you make an "ass" out of "u" and "me").
-
- // usage: (as an example) GetMenuItemFromKeyEquiv( GetMenuHandle( mEdit ), 'X' );
- // and do watch case-sensativity of the keyEquiv!!
-
- short GetMenuItemFromKeyEquiv(MenuRef menu, short keyEquiv)
- {
- short nItems = CountMItems(menu);
- short item;
- short cmd;
-
- // cycle through the menu getting each item's associated cmdKey equiv (if any)
-
- for ( item = 1; item <= nItems ; item++ )
- {
- GetItemCmd(menu, item, &cmd);
-
- // if the gotten item's cmd key equiv == the desired keyEquiv, return it
-
- if (cmd == keyEquiv)
- return item;
- }
- return 0;
- }
-
- // see HASMovableModal.h for more overall explanation of this function
-
- pascal void DisableMenuBar(short editMenuID, short hmnuID)
- {
- DialogRef dialog;
- MenuListHandle menuList;
- MenuRef menu;
- short nMenus;
- short i;
- short menuID;
- Boolean needEditMenu;
-
- // get a DialogRef to the dialog (we should be able to safely assume the dialog
- // will be the front window since you should call DisableMenuBar as per
- // the directions in HASMovableModal.h)
-
- dialog = (DialogRef)FrontWindow();
-
- // might we need the Edit menu? if we have any edittext fields (and we have a dialog)
- // we will.
-
- needEditMenu = (dialog != NULL) && (GetDialogKeyboardFocusItem(dialog) >= 0);
-
- // grab the list of all the menus
-
- menuList = (MenuListHandle) LMGetMenuList();
-
- // figure out how many menus are in the list
-
- nMenus = (*menuList)->offsetToLastMenu / (short) sizeof(MenuEntry);
-
- // initialize our global menu bar state variable
-
- barState.mbsBarEnable = 0UL;
- barState.mbsEditEnable = 0UL;
- barState.mbsEditMenuID = 0;
- barState.mbsCutItem = 0;
- barState.mbsCopyItem = 0;
- barState.mbsPasteItem = 0;
- barState.mbsIsDisabled = true;
-
- // cycle through all the menus collecting information and disabling things as
- // needed
-
- for ( i = 0; i < nMenus; i++ )
- {
- // get the menu
-
- menu = (*menuList)->theMenus[i].hMenu;
-
- // get it's id
-
- menuID = (*menu)->menuID;
-
- // if it's a system menu (e.g. application, help, keyboard) ignore it and
- // continue
-
- if (menuID <= kSystemMenuThreshold)
- continue;
-
- if ((needEditMenu) && (menuID == editMenuID))
- {
- // if we need the edit menu, save off some information about it
-
- barState.mbsEditMenuID = editMenuID;
- barState.mbsEditEnable = (*menu)->enableFlags;
- barState.mbsCutItem = GetMenuItemFromKeyEquiv(menu, 'X');
- barState.mbsCopyItem = GetMenuItemFromKeyEquiv(menu, 'C');
- barState.mbsPasteItem = GetMenuItemFromKeyEquiv(menu, 'V');
- (*menu)->enableFlags = 1UL + (1UL << barState.mbsCutItem) + (1UL << barState.mbsCopyItem) + (1UL << barState.mbsPasteItem);
- continue;
- }
-
- // check to see if the menu is enabled (in bit zero, if the bit is set/on,
- // the menu is enabled)
-
- if ((*menu)->enableFlags & 0x00000001)
- {
- // if so, disable it
-
- barState.mbsBarEnable |= (1L << i);
- DisableItem(menu, 0);
- }
-
- // if there is a hmnu resource (balloon help strings) for this menu,
- // then set it up.
-
- HMSetMenuResID(menuID, hmnuID);
- } /* for */
-
- // unhilight the menu
-
- HiliteMenu(0);
-
- // and redraw the new menubar
-
- DrawMenuBar();
-
- return;
- }
-
- // see HASMovableModal.h for more overall information about this function
-
- pascal void ReEnableMenuBar()
- {
- MenuListHandle menuList;
- MenuRef menu;
- short menuID;
- short nMenus;
- short i;
-
- // get the list of menus
-
- menuList = (MenuListHandle) LMGetMenuList();
-
- // find out how many menus are in the menubar
-
- nMenus = (*menuList)->offsetToLastMenu / (short) sizeof(MenuEntry);
-
- // if the barState isn't disabled, just return (nothing else to do)
-
- if (!barState.mbsIsDisabled)
- return;
-
- // now cycle through the menus and reenable stuff
-
- for ( i = 0; i < nMenus; i++ )
- {
- // get the menu
-
- menu = (*menuList)->theMenus[i].hMenu;
-
- // get the menu's id
-
- menuID = (*menu)->menuID;
-
- // if the menu is a system menu, ignore it and continue
-
- if (menuID <= kSystemMenuThreshold)
- continue;
-
- // enable things based upon the saved states
-
- if (menuID == barState.mbsEditMenuID)
- (*menu)->enableFlags = barState.mbsEditEnable;
- else if (barState.mbsBarEnable & (1UL << i))
- EnableItem(menu, 0);
-
- // remove the help menu strings
-
- HMSetMenuResID(menuID, -1);
- } /* for */
-
- // and draw the new menu bar
-
- DrawMenuBar();
-
- return;
- }
-
- // does just that...based upon a menu choice (by mouseDown in the menu bar or
- // a cmd key equiv), do the menu command
-
- Boolean DoMenuChoice(DialogRef dialog, short *item, long menuChoice)
- {
- short menuID = (menuChoice & 0xFFFF0000) >> 16;
- short menuItem = (menuChoice & 0x0000FFFF);
- short currentEditField;
- short itemType;
- Handle itemHandle;
- Rect itemRect;
-
- // unhilite the menu bar
-
- HiliteMenu(0);
-
- // we only handle menu commands from the edit menu, and then, only cut, copy, and
- // paste
-
- if (menuID == barState.mbsEditMenuID)
- {
- // find out what the current/active edit field is
-
- currentEditField = GetDialogKeyboardFocusItem(dialog);
-
- GetDialogItem(dialog, currentEditField, &itemType, &itemHandle, &itemRect);
- if ((itemType & kItemDisableBit) == 0)
- *item = currentEditField;
-
- // and depending on what the desired command is, do it
-
- if (menuItem == barState.mbsCutItem)
- {
- DialogCut(dialog);
- ZeroScrap();
- TEToScrap();
- }
- else if (menuItem == barState.mbsCopyItem)
- {
- DialogCopy(dialog);
- ZeroScrap();
- TEToScrap();
- }
- else if (menuItem == barState.mbsPasteItem)
- {
- DialogPaste(dialog);
- }
-
- return true;
- } /* if menuID == mbsEditMenuID */
- else
- return false;
- }
-
- #pragma mark -
- #pragma mark ••• Events •••
-
- // our mouseDown event handler
-
- Boolean HandleMouseDown(DialogRef dialog, EventRecord *event, short *item)
- {
- WindowRef window;
- short partCode;
- SoundUPP beeper;
- Rect dragRect;
- RgnHandle windStrucRgn;
-
- // find the window clicked on (or at least, where the mouseDown occured)
-
- partCode = FindWindow(event->where, &window);
-
- // if in the menuBar, do the command and return
-
- if (partCode == inMenuBar)
- return DoMenuChoice(dialog, item, MenuSelect(event->where));
-
- // COPLAND - the following line breaks under STRICT macros
- // if (!PtInRgn(event->where, ((WindowPeek)dialog)->strucRgn))
-
- // since it breaks under STRICT/Copland stuff, here's how we do it.
-
- // first, get a new region
-
- windStrucRgn = NewRgn();
-
- // now, get the structure region of our dialog
-
- GetWindowStructureRgn( GetDialogWindow( dialog ), windStrucRgn );
-
- // if the mouseDown didn't occur within our dialog (and also, not within the
- // menubar, but we took care of the menubar case above so no additional need
- // to check for it here)....
-
- if ( !PtInRgn( event->where, windStrucRgn ) )
- {
- // and if it was within our application's layer, beep (like if they clicked
- // on another one of the app's windows...but of course, we do allow layer/context
- // switching...joys of multitasking!)
-
- // get the beep sound
-
- beeper = (SoundUPP) LMGetDABeeper();
-
- // if we have it, beep
-
- if (beeper)
- CallSoundProc(beeper, 1);
-
- // turn our event into a null event
-
- event->what = nullEvent;
-
- // and dispose of our region
-
- if ( windStrucRgn != nil )
- {
- DisposeRgn( windStrucRgn );
-
- // i want to make sure the windStrucRgn variable is set to nil cause
- // later on we again check if (windStrucRgn != nil) and we don't want
- // the chance of disposing of an already disposed of region!
-
- windStrucRgn = nil;
- }
- return false;
- }
-
- // if they clicked in the drag region (in the title bar)...
-
- if ((partCode == inDrag) && (dialog == (DialogRef)window))
- {
- // set the boundries of the possible area to drag within to all the
- // monitor real estate we have (this is nice for multiple monitor setups).
-
- // but one note...getting the dragRect this way assumes we have color quickdraw
- // on the computer.
-
- // dragRect = (*GetGrayRgn())->rgnBBox;
-
- // so, to be a bit more proper just in case we want to use this code on machines
- // that might not have color quickdraw (this will soon probably be a moot thing
- // to check for), let's do this:
-
- long response;
-
- // check for the presence of color quickdraw
-
- if ((Gestalt(gestaltQuickdrawVersion, &response) == noErr) && (response >= gestalt8BitQD))
- {
- // if no gestalt errors and we're at least in color qd
- // then we can set it this way (once again, to happily handle multi monitor
- // setups)
-
- dragRect = (*GetGrayRgn())->rgnBBox;
- }
- else
- {
- // no color qd or some gestalt problem...so we'll use the good old screenBits.bounds
- // (as a note, just using this can still work in multiple monitor setups, but
- // that's only due to a kludge that Apple wrote into the system software...
- // it's messy, and someday Apple will probably remove it)
-
- dragRect = qd.screenBits.bounds;
- }
-
- // that was something I added into this (Marco just had the straight GetGrayRgn thing)
- // and i did this as per some "morally correct" code Eric Shapiro gave in the
- // July 1994 MacTech Magazine. I do this same thing elsewhere in Hsoi's App
- // Shell, in HsoiDoDrag()
-
- // now just let the toolbox handle the drag
-
- DragWindow(window, event->where, &dragRect);
-
- // and make the event a null event
-
- event->what = nullEvent;
- }
-
- // once again, if we have a region, dispose of it (we don't need no stinkin' memory leaks!)
-
- if ( windStrucRgn != nil )
- DisposeRgn( windStrucRgn );
-
- return false;
- }
-
- #pragma mark -
- #pragma mark ••• MovableModalDialog •••
-
- // and the meat of it all...the routine that actually does the movable modal dialog.
- // do see HASMovableModal.h for more information.
-
- pascal void MovableModalDialog(ModalFilterUPP filterProc, short *item)
- {
- GrafPtr savePort;
- DialogRef dialog, junkDialog;
- EventRecord event;
-
- // hopefully, any routine calling this function will have already stopped the
- // sound, but just in case, we'll put this here. (this is from Michael Kamprath's
- // WASTE Object Handlers)
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // initalize our item to zero
-
- *item = 0;
-
- // get a ref to our dialog (we will assume it's frontmost)
-
- dialog = (DialogRef)FrontWindow();
-
- // if we don't have it, something's dead wrong
-
- if (!dialog)
- return;
-
- // set up the ports
-
- GetPort(&savePort);
- SetGrafPortOfDialog(dialog);
-
- // and now loop until something exciting happens.
-
- do {
-
- // get the next event as filtered by our event mask
-
- WaitNextEvent(kMovableModalEventMask, &event, 0, NULL);
-
- // call the filter
-
- if (filterProc && CallModalFilterProc(filterProc, dialog, &event, item))
- break;
-
- // if we have a mouseDown event and we handle it right...
-
- if ((event.what == mouseDown) && HandleMouseDown(dialog, &event, item))
- break;
-
- // a keydown, with the command key, and do the menu choice then...
-
- if ((event.what == keyDown) && (event.modifiers & cmdKey) && DoMenuChoice(dialog, item, MenuKey(event.message & charCodeMask)))
- break;
-
- // and finally see if the event pertains to our dialog, then find the
- // dialog and process the event
-
- if (IsDialogEvent(&event) && DialogSelect(&event, &junkDialog, item))
- break;
- } while (true);
-
- // restore the port, and that's it!
-
- SetPort(savePort);
-
- return;
- }